//// Copyright 2000-2023 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
//package com.linqingying.gitcode.pullrequest.ui.timeline
//
//import com.intellij.collaboration.async.launchNow
//import com.intellij.collaboration.async.mapDataToModel
//import com.intellij.collaboration.async.nestedDisposable
//import com.intellij.collaboration.ui.html.AsyncHtmlImageLoader
//import com.intellij.collaboration.util.ChangesSelection
//import com.intellij.collaboration.util.getOrNull
//import com.intellij.openapi.actionSystem.DataKey
//import com.intellij.openapi.project.Project
//import com.intellij.openapi.util.Disposer
//import com.intellij.platform.util.coroutines.childScope
//import kotlinx.coroutines.CoroutineName
//import kotlinx.coroutines.CoroutineScope
//import kotlinx.coroutines.Dispatchers
//import kotlinx.coroutines.channels.BufferOverflow
//import kotlinx.coroutines.channels.awaitClose
//import kotlinx.coroutines.flow.*
//import com.linqingying.gitcode.api.data.GCIssueComment
//import com.linqingying.gitcode.api.data.GCNode
//import com.linqingying.gitcode.api.data.GCRepositoryPermissionLevel
//import com.linqingying.gitcode.api.data.GCUser
//import com.linqingying.gitcode.api.data.pullrequest.GCPullRequestCommitShort
//import com.linqingying.gitcode.api.data.pullrequest.GCPullRequestReview
//import com.linqingying.gitcode.api.data.pullrequest.timeline.GCPRTimelineEvent
//import com.linqingying.gitcode.api.data.pullrequest.timeline.GCPRTimelineItem as GCPRTimelineItemDTO
//import com.linqingying.gitcode.pullrequest.data.GCListLoader
//import com.linqingying.gitcode.pullrequest.data.GCPRDataContext
//import com.linqingying.gitcode.pullrequest.data.provider.GCPRDataProvider
//import com.linqingying.gitcode.pullrequest.data.service.GCPRPersistentInteractionState.PRState
//import com.linqingying.gitcode.pullrequest.ui.GCApiLoadingErrorHandler
//import com.linqingying.gitcode.pullrequest.ui.GCLoadingErrorHandler
//import com.linqingying.gitcode.pullrequest.ui.timeline.item.GCPRTimelineItem
//import com.linqingying.gitcode.pullrequest.ui.timeline.item.UpdateableGCPRTimelineCommentViewModel
//import com.linqingying.gitcode.pullrequest.ui.timeline.item.UpdateableGCPRTimelineReviewViewModel
//import com.linqingying.gitcode.ui.avatars.GCAvatarIconsProvider
//
//interface GCPRTimelineViewModel {
//  val ghostUser: GCUser
//  val currentUser: GCUser
//
//  val detailsVm: GCPRDetailsTimelineViewModel
//
//  val timelineItems: StateFlow<List<GCPRTimelineItem>>
//  val isLoading: StateFlow<Boolean>
//  val loadingError: StateFlow<Throwable?>
//
//  val loadingErrorHandler: GCLoadingErrorHandler
//
//  val commentVm: GCPRNewCommentViewModel?
//
//  val htmlImageLoader: AsyncHtmlImageLoader
//  val avatarIconsProvider: GCAvatarIconsProvider
//
//  fun update()
//
//  fun updateAll()
//
//  fun requestMore()
//
//  fun showCommit(oid: String)
//
//  companion object {
//    val DATA_KEY: DataKey<GCPRTimelineViewModel> = DataKey.create("GitCode.PullRequest.Timeline.ViewModel")
//  }
//}
//
//internal class GCPRTimelineViewModelImpl(
//  private val project: Project,
//  parentCs: CoroutineScope,
//  private val dataContext: GCPRDataContext,
//  private val dataProvider: GCPRDataProvider
//) : GCPRTimelineViewModel {
//  private val cs = parentCs.childScope(Dispatchers.Main + CoroutineName("GitCode Pull Request Timeline View Model"))
//
//  private val securityService = dataContext.securityService
//
//  private val detailsData = dataProvider.detailsData
//  private val reviewData = dataProvider.reviewData
//  private val commentsData = dataProvider.commentsData
//
//  private val interactionState = dataContext.interactionState
//
//  override val ghostUser: GCUser = securityService.ghostUser
//  override val currentUser: GCUser = securityService.currentUser
//
//  override val detailsVm = GCPRDetailsTimelineViewModel(project, parentCs, dataContext, dataProvider)
//  private val timelineLoader = dataProvider.acquireTimelineLoader(cs.nestedDisposable())
//
//  override val loadingErrorHandler: GCLoadingErrorHandler =
//    GCApiLoadingErrorHandler(project, securityService.account, timelineLoader::reset)
//
//  override val commentVm: GCPRNewCommentViewModel? =
//    if (securityService.currentUserHasPermissionLevel(GCRepositoryPermissionLevel.READ)) {
//      GCPRNewCommentViewModel(project, parentCs, commentsData)
//    }
//    else null
//
//  override val htmlImageLoader = dataContext.htmlImageLoader
//  override val avatarIconsProvider = dataContext.avatarIconsProvider
//
//  override val timelineItems: StateFlow<List<GCPRTimelineItem>>
//
//  override val isLoading: StateFlow<Boolean> = callbackFlow {
//    val disposable = Disposer.newDisposable()
//    timelineLoader.addLoadingStateChangeListener(disposable) {
//      trySend(timelineLoader.loading)
//
//      // Update the last seen date to the last time a fully loaded timeline was loaded
//      if (!timelineLoader.canLoadMore() && !timelineLoader.loading) {
//        updateLastSeen(System.currentTimeMillis())
//      }
//    }
//    send(timelineLoader.loading)
//    awaitClose { Disposer.dispose(disposable) }
//  }.stateIn(cs, SharingStarted.Eagerly, timelineLoader.loading)
//
//  override val loadingError: StateFlow<Throwable?> = callbackFlow {
//    val disposable = Disposer.newDisposable()
//    timelineLoader.addErrorChangeListener(disposable) {
//      trySend(timelineLoader.error)
//    }
//    send(timelineLoader.error)
//    awaitClose { Disposer.dispose(disposable) }
//  }.stateIn(cs, SharingStarted.Eagerly, timelineLoader.error)
//
//  val showCommitRequests = MutableSharedFlow<String>(extraBufferCapacity = 1, onBufferOverflow = BufferOverflow.DROP_OLDEST)
//  val showDiffRequests = MutableSharedFlow<ChangesSelection>(extraBufferCapacity = 1, onBufferOverflow = BufferOverflow.DROP_OLDEST)
//
//  init {
//    val timelineModel = GCPRTimelineMergingModel()
//    timelineModel.add(timelineLoader.loadedData)
//    val itemsFromModel = MutableStateFlow(timelineModel.getItemsList())
//    timelineItems = itemsFromModel.mapDataToModel(
//      ::getItemID,
//      { createItemFromDTO(it) },
//      { update(it) }
//    ).stateIn(cs, SharingStarted.Eagerly, emptyList())
//
//    timelineLoader.addDataListener(cs.nestedDisposable(), object : GCListLoader.ListDataListener {
//      override fun onDataAdded(startIdx: Int) {
//        val loadedData = timelineLoader.loadedData
//        val addedData = loadedData.subList(startIdx, loadedData.size)
//        timelineModel.add(addedData)
//        itemsFromModel.value = timelineModel.getItemsList()
//
//        val latestLoadedItemTime = loadedData.mapNotNull { it.createdAt }.maxOrNull()?.time
//        updateLastSeen(latestLoadedItemTime ?: System.currentTimeMillis())
//      }
//
//      override fun onDataUpdated(idx: Int) {
//        val newItem = timelineLoader.loadedData[idx]
//        timelineModel.update(idx, newItem)
//        itemsFromModel.value = timelineModel.getItemsList()
//
//        updateLastSeen(System.currentTimeMillis())
//      }
//
//      override fun onDataRemoved(idx: Int) {
//        timelineModel.remove(idx)
//        itemsFromModel.value = timelineModel.getItemsList()
//
//        updateLastSeen(System.currentTimeMillis())
//      }
//
//      override fun onAllDataRemoved() {
//        timelineModel.removeAll()
//        itemsFromModel.value = timelineModel.getItemsList()
//        timelineLoader.loadMore()
//      }
//    })
//  }
//
//  private fun updateLastSeen(lastSeenMillis: Long) {
//    val prId = detailsVm.details.value.getOrNull()?.id ?: return
//    interactionState.updateStateFor(prId) { st ->
//      PRState(prId, maxOf(lastSeenMillis, st?.lastSeen ?: 0L))
//    }
//  }
//
//  private fun getItemID(data: GCPRTimelineItemDTO): Any =
//    when (data) {
//      is GCNode -> data.id
//      else -> data
//    }
//
//  private fun CoroutineScope.createItemFromDTO(data: GCPRTimelineItemDTO): GCPRTimelineItem =
//    when (data) {
//      is GCIssueComment -> {
//        UpdateableGCPRTimelineCommentViewModel(project, this, dataContext, dataProvider.commentsData, data)
//      }
//      is GCPullRequestReview -> {
//        UpdateableGCPRTimelineReviewViewModel(project, this, dataContext, dataProvider, data).also {
//          launchNow {
//            it.showDiffRequests.collect(showDiffRequests)
//          }
//        }
//      }
//      is GCPullRequestCommitShort -> GCPRTimelineItem.Commits(listOf(data))
//      is GCPRTimelineGroupedCommits -> GCPRTimelineItem.Commits(data.items)
//      is GCPRTimelineEvent -> GCPRTimelineItem.Event(data)
//      else -> GCPRTimelineItem.Unknown("")
//    }
//
//  private fun GCPRTimelineItem.update(data: GCPRTimelineItemDTO) {
//    if (this is UpdateableGCPRTimelineCommentViewModel && data is GCIssueComment) {
//      update(data)
//    }
//  }
//
//  override fun requestMore() {
//    timelineLoader.loadMore()
//  }
//
//  override fun update() {
//    if (timelineLoader.loadedData.isNotEmpty())
//      timelineLoader.loadMore(true)
//  }
//
//  override fun updateAll() {
//    detailsData.reloadDetails()
//    timelineLoader.loadMore(true)
//    reviewData.resetReviewThreads()
//  }
//
//  override fun showCommit(oid: String) {
//    showCommitRequests.tryEmit(oid)
//  }
//}
//
//private fun GCPRTimelineMergingModel.getItemsList(): List<GCPRTimelineItemDTO> =
//  buildList {
//    for (i in 0 until getSize()) {
//      add(getElementAt(i))
//    }
//  }
